// Copyright 2016 The Cockroach Authors.
// Copyright (c) 2022-present, Shanghai Yunxi Technology Co, Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This software (KWDB) is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package duration

import (
	"fmt"
	"math"
	"testing"
	"time"

	_ "gitee.com/kwbasedb/kwbase/pkg/util/log"
)

type durationTest struct {
	cmpToPrev int
	duration  Duration
	err       bool
}

// positiveDurationTests is used both to check that each duration roundtrips
// through Encode/Decode and that they sort in the expected way. They are not
// required to be listed in ascending order, but for ease of maintenance, it is
// expected that they stay ascending.
//
// The negative tests are generated by prepending everything but the 0 case and
// flipping the sign of cmpToPrev, since they will be getting bigger in absolute
// value and more negative.
//
// TODO(dan): Write more tests with a mixture of positive and negative
// components.
var positiveDurationTests = []durationTest{
	{1, Duration{Months: 0, Days: 0, nanos: 0}, false},
	{1, Duration{Months: 0, Days: 0, nanos: 1}, false},
	{1, Duration{Months: 0, Days: 0, nanos: nanosInDay - 1}, false},
	{1, Duration{Months: 0, Days: 1, nanos: 0}, false},
	{0, Duration{Months: 0, Days: 0, nanos: nanosInDay}, false},
	{1, Duration{Months: 0, Days: 0, nanos: nanosInDay + 1}, false},
	{1, Duration{Months: 0, Days: DaysPerMonth - 1, nanos: 0}, false},
	{1, Duration{Months: 0, Days: 0, nanos: nanosInMonth - 1}, false},
	{1, Duration{Months: 1, Days: 0, nanos: 0}, false},
	{0, Duration{Months: 0, Days: DaysPerMonth, nanos: 0}, false},
	{0, Duration{Months: 0, Days: 0, nanos: nanosInMonth}, false},
	{1, Duration{Months: 0, Days: 0, nanos: nanosInMonth + 1}, false},
	{1, Duration{Months: 0, Days: DaysPerMonth + 1, nanos: 0}, false},
	{1, Duration{Months: 1, Days: 1, nanos: 1}, false},
	{1, Duration{Months: 1, Days: 10, nanos: 0}, false},
	{0, Duration{Months: 0, Days: 40, nanos: 0}, false},
	{1, Duration{Months: 2, Days: 0, nanos: 0}, false},
	{1, Duration{Months: math.MaxInt64 - 1, Days: DaysPerMonth - 1, nanos: nanosInDay * 2}, true},
	{1, Duration{Months: math.MaxInt64 - 1, Days: DaysPerMonth * 2, nanos: nanosInDay * 2}, true},
	{1, Duration{Months: math.MaxInt64, Days: math.MaxInt64, nanos: nanosInMonth + nanosInDay}, true},
	{1, Duration{Months: math.MaxInt64, Days: math.MaxInt64, nanos: math.MaxInt64}, true},
}

func fullDurationTests() []durationTest {
	var ret []durationTest
	for _, test := range positiveDurationTests {
		d := test.duration
		negDuration := Duration{Months: -d.Months, Days: -d.Days, nanos: -d.nanos}
		ret = append(ret, durationTest{cmpToPrev: -test.cmpToPrev, duration: negDuration, err: test.err})
	}
	ret = append(ret, positiveDurationTests...)
	return ret
}

func TestEncodeDecode(t *testing.T) {
	for i, test := range fullDurationTests() {
		sortNanos, months, days, err := test.duration.Encode()
		if test.err && err == nil {
			t.Errorf("%d expected error but didn't get one", i)
		} else if !test.err && err != nil {
			t.Errorf("%d expected no error but got one: %s", i, err)
		}
		if err != nil {
			continue
		}
		sortNanosBig, _, _ := test.duration.EncodeBigInt()
		if sortNanos != sortNanosBig.Int64() {
			t.Errorf("%d Encode and EncodeBig didn't match [%d] vs [%s]", i, sortNanos, sortNanosBig)
		}
		d, err := Decode(sortNanos, months, days)
		if err != nil {
			t.Fatal(err)
		}
		if test.duration != d {
			t.Errorf("%d encode/decode mismatch [%v] vs [%v]", i, test, d)
		}
	}
}

func TestCompare(t *testing.T) {
	prev := Duration{nanos: 1} // It's expected that we start with something greater than 0.
	for i, test := range fullDurationTests() {
		cmp := test.duration.Compare(prev)
		if cmp != test.cmpToPrev {
			t.Errorf("%d did not compare correctly got %d expected %d [%s] vs [%s]", i, cmp, test.cmpToPrev, prev, test.duration)
		}
		prev = test.duration
	}
}

func TestNormalize(t *testing.T) {
	for i, test := range fullDurationTests() {
		nanos, _, _ := test.duration.EncodeBigInt()
		normalized := test.duration.normalize()
		normalizedNanos, _, _ := normalized.EncodeBigInt()
		if nanos.Cmp(normalizedNanos) != 0 {
			t.Errorf("%d effective nanos were changed [%s] [%s]", i, test.duration, normalized)
		}
		if normalized.Days > DaysPerMonth && normalized.Months != math.MaxInt64 ||
			normalized.Days < -DaysPerMonth && normalized.Months != math.MinInt64 {
			t.Errorf("%d days were not normalized [%s]", i, normalized)
		}
		if normalized.nanos > nanosInDay && normalized.Days != math.MaxInt64 ||
			normalized.nanos < -nanosInDay && normalized.Days != math.MinInt64 {
			t.Errorf("%d nanos were not normalized [%s]", i, normalized)
		}
	}
}

func TestDiffMicros(t *testing.T) {
	tests := []struct {
		t1, t2  time.Time
		expDiff int64
	}{
		{
			t1:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 0,
		},
		{
			t1:      time.Date(1, 8, 15, 12, 30, 45, 0, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: -63062710155000000,
		},
		{
			t1:      time.Date(1994, 8, 15, 12, 30, 45, 0, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: -169730955000000,
		},
		{
			t1:      time.Date(2012, 8, 15, 12, 30, 45, 0, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 398349045000000,
		},
		{
			t1:      time.Date(8012, 8, 15, 12, 30, 45, 0, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 189740061045000000,
		},
		// Test if the nanoseconds round correctly.
		{
			t1:      time.Date(2000, 1, 1, 0, 0, 0, 499, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 0,
		},
		{
			t1:      time.Date(1999, 12, 31, 23, 59, 59, 999999501, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 0,
		},
		{
			t1:      time.Date(2000, 1, 1, 0, 0, 0, 500, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: 1,
		},
		{
			t1:      time.Date(1999, 12, 31, 23, 59, 59, 999999500, time.UTC),
			t2:      time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			expDiff: -1,
		},
	}

	for i, test := range tests {
		if res := DiffMicros(test.t1, test.t2); res != test.expDiff {
			t.Errorf("%d: expected DiffMicros(%v, %v) = %d, found %d",
				i, test.t1, test.t2, test.expDiff, res)
		} else {
			// Swap order and make sure the results are mirrored.
			exp := -test.expDiff
			if res := DiffMicros(test.t2, test.t1); res != exp {
				t.Errorf("%d: expected DiffMicros(%v, %v) = %d, found %d",
					i, test.t2, test.t1, exp, res)
			}
		}
	}
}

// TestAdd looks at various rounding cases, comparing our date math
// to behavior observed in PostgreSQL 10.
func TestAdd(t *testing.T) {
	tests := []struct {
		t   time.Time
		d   Duration
		exp time.Time
	}{
		// Year wraparound
		{
			t:   time.Date(1993, 10, 01, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 3},
			exp: time.Date(1994, 1, 01, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(1992, 10, 01, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 15},
			exp: time.Date(1994, 1, 01, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 28, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 12},
			exp: time.Date(2021, time.July, 28, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 18},
			exp: time.Date(2022, time.January, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 30},
			exp: time.Date(2023, time.January, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 12},
			exp: time.Date(2021, time.July, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 12, nanos: int64(10 * time.Second)},
			exp: time.Date(2021, time.July, 29, 0, 0, 10, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 17, nanos: int64(10 * time.Second)},
			exp: time.Date(2021, time.December, 29, 0, 0, 10, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 18, nanos: int64(10 * time.Second)},
			exp: time.Date(2022, time.January, 29, 0, 0, 10, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 19, nanos: int64(10 * time.Second)},
			exp: time.Date(2022, time.February, 28, 0, 0, 10, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 20, nanos: int64(10 * time.Second)},
			exp: time.Date(2022, time.March, 29, 0, 0, 10, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 30, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 12},
			exp: time.Date(2021, time.July, 30, 0, 0, 0, 0, time.UTC),
		},

		// Check leap behaviors
		{
			t:   time.Date(1996, 02, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 12},
			exp: time.Date(1997, 02, 28, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(1996, 02, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 48},
			exp: time.Date(2000, 02, 29, 0, 0, 0, 0, time.UTC),
		},

		// This pair shows something one might argue is weird:
		// that two different times plus the same duration results
		// in the same result.
		{
			t:   time.Date(1996, 01, 30, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 1, Days: 1},
			exp: time.Date(1996, 03, 01, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(1996, 01, 31, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: 1, Days: 1},
			exp: time.Date(1996, 03, 01, 0, 0, 0, 0, time.UTC),
		},

		// Check negative operations
		{
			t:   time.Date(2016, 02, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1},
			exp: time.Date(2016, 01, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2016, 02, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1, Days: -1},
			exp: time.Date(2016, 01, 28, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2016, 03, 31, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1},
			exp: time.Date(2016, 02, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2016, 03, 31, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1, Days: -1},
			exp: time.Date(2016, 02, 28, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2016, 02, 01, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1},
			exp: time.Date(2016, 01, 01, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2016, 02, 01, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -1, Days: -1},
			exp: time.Date(2015, 12, 31, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 28, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -12},
			exp: time.Date(2019, time.July, 28, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -18},
			exp: time.Date(2019, time.January, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -30},
			exp: time.Date(2018, time.January, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -12},
			exp: time.Date(2019, time.July, 29, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -12, nanos: int64(-10 * time.Second)},
			exp: time.Date(2019, time.July, 28, 23, 59, 50, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -19, nanos: int64(-10 * time.Second)},
			exp: time.Date(2018, time.December, 28, 23, 59, 50, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 29, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -20, nanos: int64(-10 * time.Second)},
			exp: time.Date(2018, time.November, 28, 23, 59, 50, 0, time.UTC),
		},
		{
			t:   time.Date(2020, time.July, 30, 0, 0, 0, 0, time.UTC),
			d:   Duration{Months: -12},
			exp: time.Date(2019, time.July, 30, 0, 0, 0, 0, time.UTC),
		},
	}
	for i, test := range tests {
		t.Run(fmt.Sprintf("%s_%s", test.t, test.d), func(t *testing.T) {
			if res := Add(test.t, test.d); !test.exp.Equal(res) {
				t.Errorf("%d: expected Add(%s, %s) = %s, found %s",
					i, test.t, test.d, test.exp, res)
			}
		})
	}
}

func TestAddMicros(t *testing.T) {
	tests := []struct {
		t   time.Time
		d   int64
		exp time.Time
	}{
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   0,
			exp: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   123456789,
			exp: time.Date(2000, 1, 1, 0, 2, 3, 456789000, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 999, time.UTC),
			d:   123456789,
			exp: time.Date(2000, 1, 1, 0, 2, 3, 456789999, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   12345678987654321,
			exp: time.Date(2391, 03, 21, 19, 16, 27, 654321000, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   math.MaxInt64 / 10,
			exp: time.Date(31227, 9, 14, 2, 48, 05, 477580000, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   -123456789,
			exp: time.Date(1999, 12, 31, 23, 57, 56, 543211000, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 999, time.UTC),
			d:   -123456789,
			exp: time.Date(1999, 12, 31, 23, 57, 56, 543211999, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   -12345678987654321,
			exp: time.Date(1608, 10, 12, 04, 43, 32, 345679000, time.UTC),
		},
		{
			t:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
			d:   -math.MaxInt64 / 10,
			exp: time.Date(-27228, 04, 18, 21, 11, 54, 522420000, time.UTC),
		},
	}

	for i, test := range tests {
		if res := AddMicros(test.t, test.d); !test.exp.Equal(res) {
			t.Errorf("%d: expected AddMicros(%v, %d) = %v, found %v",
				i, test.t, test.d, test.exp, res)
		}
	}
}

func TestFloatMath(t *testing.T) {
	const nanosInMinute = nanosInSecond * 60
	const nanosInHour = nanosInMinute * 60

	tests := []struct {
		d   Duration
		f   float64
		mul Duration
		div Duration
	}{
		{
			Duration{Months: 1, Days: 2, nanos: nanosInHour * 2},
			0.15,
			Duration{Days: 4, nanos: nanosInHour*19 + nanosInMinute*30},
			Duration{Months: 6, Days: 33, nanos: nanosInHour*21 + nanosInMinute*20},
		},
		{
			Duration{Months: 1, Days: 2, nanos: nanosInHour * 2},
			0.3,
			Duration{Days: 9, nanos: nanosInHour * 15},
			Duration{Months: 3, Days: 16, nanos: nanosInHour*22 + nanosInMinute*40},
		},
		{
			Duration{Months: 1, Days: 2, nanos: nanosInHour * 2},
			0.5,
			Duration{Days: 16, nanos: nanosInHour * 1},
			Duration{Months: 2, Days: 4, nanos: nanosInHour * 4},
		},
		{
			Duration{Months: 1, Days: 2, nanos: nanosInHour * 2},
			0.8,
			Duration{Days: 25, nanos: nanosInHour * 16},
			Duration{Months: 1, Days: 10, nanos: nanosInHour*2 + nanosInMinute*30},
		},
		{
			Duration{Months: 1, Days: 17, nanos: nanosInHour * 2},
			2.0,
			Duration{Months: 2, Days: 34, nanos: nanosInHour * 4},
			Duration{Days: 23, nanos: nanosInHour * 13},
		},
	}

	for i, test := range tests {
		if res := test.d.MulFloat(test.f); test.mul != res {
			t.Errorf(
				"%d: expected %v.MulFloat(%f) = %v, found %v",
				i,
				test.d,
				test.f,
				test.mul,
				res)
		}
		if res := test.d.DivFloat(test.f); test.div != res {
			t.Errorf(
				"%d: expected %v.DivFloat(%f) = %v, found %v",
				i,
				test.d,
				test.f,
				test.div,
				res)
		}
	}
}

func TestTruncate(t *testing.T) {
	zero := time.Duration(0).String()
	testCases := []struct {
		d, r time.Duration
		s    string
	}{
		{0, 1, zero},
		{0, 1, zero},
		{time.Second, 1, "1s"},
		{time.Second, 2 * time.Second, zero},
		{time.Second + 1, time.Second, "1s"},
		{11 * time.Nanosecond, 10 * time.Nanosecond, "10ns"},
		{time.Hour + time.Nanosecond + 3*time.Millisecond + time.Second, time.Millisecond, "1h0m1.003s"},
	}
	for i, tc := range testCases {
		if s := Truncate(tc.d, tc.r).String(); s != tc.s {
			t.Errorf("%d: (%s,%s) should give %s, but got %s", i, tc.d, tc.r, tc.s, s)
		}
	}
}

// TestNanos verifies that nanoseconds can only be present after Decode and
// that any operation will remove them.
func TestNanos(t *testing.T) {
	d, err := Decode(1, 0, 0)
	if err != nil {
		t.Fatal(err)
	}
	if expect, actual := int64(1), d.nanos; expect != actual {
		t.Fatalf("expected %d, got %d", expect, actual)
	}
	if expect, actual := "00:00:00.000000001+1ns", d.StringNanos(); expect != actual {
		t.Fatalf("expected %s, got %s", expect, actual)
	}
	// Add, even of a 0-duration interval, should call round.
	d = d.Add(Duration{})
	if expect, actual := int64(1), d.nanos; expect != actual {
		t.Fatalf("expected %d, got %d", expect, actual)
	}
	d, err = Decode(500, 0, 0)
	if err != nil {
		t.Fatal(err)
	}
	if expect, actual := int64(500), d.nanos; expect != actual {
		t.Fatalf("expected %d, got %d", expect, actual)
	}
	if expect, actual := "00:00:00.0000005+500ns", d.StringNanos(); expect != actual {
		t.Fatalf("expected %s, got %s", expect, actual)
	}
	d = d.Add(Duration{})
	if expect, actual := int64(500), d.nanos; expect != actual {
		t.Fatalf("expected %d, got %d", expect, actual)
	}
	if expect, actual := "00:00:00.0000005+500ns", d.StringNanos(); expect != actual {
		t.Fatalf("expected %s, got %s", expect, actual)
	}
}

func BenchmarkAdd(b *testing.B) {
	b.Run("fast-path-by-no-months-in-duration", func(b *testing.B) {
		s := time.Date(2018, 01, 01, 0, 0, 0, 0, time.UTC)
		d := Duration{Days: 1}
		for i := 0; i < b.N; i++ {
			Add(s, d)
		}
	})
	b.Run("fast-path-by-day-number", func(b *testing.B) {
		s := time.Date(2018, 01, 01, 0, 0, 0, 0, time.UTC)
		d := Duration{Months: 1}
		for i := 0; i < b.N; i++ {
			Add(s, d)
		}
	})
	b.Run("no-adjustment", func(b *testing.B) {
		s := time.Date(2018, 01, 31, 0, 0, 0, 0, time.UTC)
		d := Duration{Months: 2}
		for i := 0; i < b.N; i++ {
			Add(s, d)
		}
	})
	b.Run("with-adjustment", func(b *testing.B) {
		s := time.Date(2018, 01, 31, 0, 0, 0, 0, time.UTC)
		d := Duration{Months: 1}
		for i := 0; i < b.N; i++ {
			Add(s, d)
		}
	})
}
